/*
 * @Author: Clark
 * @Email: haixuanwoTxh@gmail.com
 * @Date: 2021-12-10 15:13:24
 * @LastEditors: Clark
 * @LastEditTime: 2021-12-12 13:17:31
 * @Description: file content
 */

#include "protocol_engine.h"

#include <unistd.h>
#include <string.h>

ProtocolEngine::ProtocolEngine()
{
    idManager = new IdManager;
    clientManager = new ClientManager;
    talkProtocol = new TalkProtocol;

    std::string fileName = XML_FILE_NAME;
    xmlFile = new XmlFile(fileName);

    User user;
    user.loginStatus = UNLOGGED;

    unsigned int userCount = xmlFile->get_user_count();
    for (unsigned int i = 1; i <= userCount; i++)
    {
        if (xmlFile->get_user_by_index(i, user))
        {
            clientManager->add_user(user);
        }
    }
}

void ProtocolEngine::establish_connection()
{

}

/**
 * @brief 连接断开时的状态变更
 * @param clientFd  对应连接的socket fd
 */
void ProtocolEngine::disconnect(int clientFd)
{
    u_int32_t ID = 0;
    if(idManager->find_userID(clientFd, ID))
    {
        clientManager->set_status(ID, UNLOGGED);
        idManager->delete_id(clientFd);
    }

    public_online_client();
}

/**
 * @brief 客户端注册
 * @param clientFd 对应连接的socket fd
 * @param data 注册数据
 * @param dataLen 数据长度
 * @return true 成功
 * @return false 失败
 */
bool ProtocolEngine::logup(int clientFd, unsigned char *data, int dataLen)
{
    int result = false;
    std::string account;
    std::string passwd;
    unsigned char res[2]={0};
    unsigned short logupResult = FAIL;
    unsigned char packet[1024] = {0};
    int packetLen = -1;

    result = talkProtocol->get_name_passwd(data, dataLen, account, passwd);
    if (!result)
    {
        printf("logup get account fail\n");
        return false;
    }

    printf("%s: LOGUP account[%s] passwd[%s] \n", __func__, account.c_str(), passwd.c_str());
    talkProtocol->print_data_as_hex(data, dataLen);

    User user;
    if (false == clientManager->find_user_by_account(account, user))
    {
        // 保存新用户
        User newUser;
        newUser.ID = clientManager->get_new_user_ID();
        newUser.account = account;
        newUser.passwd = passwd;
        newUser.loginStatus = UNLOGGED;

        clientManager->add_user(newUser);
        xmlFile->add_user(newUser);
        xmlFile->save_file();

        logupResult = OK;
        printf("%s: new user logup ok ID[%u] account[%s] passwd[%s]\n", __func__, newUser.ID, newUser.account.c_str(), newUser.passwd.c_str());
    }
    else
    {
        printf("logup fail the user already exist\n");
    }

    // 回复注册结果
    talkProtocol->set_value_to_2Byte_buf(logupResult, res);
    result = talkProtocol->build_protocol_packet(LOGUP, res, 2, packet, sizeof(packet), packetLen);
    if (!result)
    {
        printf("logup fail build response packet fail\n");
        return false;
    }

    write(clientFd, packet, packetLen);
    printf("account[%s]: logup %s\n", account.c_str(), (OK==logupResult)?"success":"fail");

    return OK==logupResult;
}

/**
 * @brief 客户端登录
 * @param clientFd 对应连接的socket fd
 * @param data 登录数据
 * @param dataLen 数据长度
 * @return true 成功
 * @return false 失败
 */
bool ProtocolEngine::login(int clientFd, unsigned char *data, int dataLen)
{
    int result = false;
    std::string account;
    std::string passwd;
    unsigned short loginResult = FAIL;
    unsigned char res[1024]={0};
    unsigned char packet[1024] = {0};
    int packetLen = -1;

    result = talkProtocol->get_name_passwd(data, dataLen, account, passwd);
    if (!result)
    {
        printf("login get account fail\n");
        return false;
    }

    printf("%s: LOGIN account[%s] passwd[%s]\n", __func__, account.c_str(), passwd.c_str());
    talkProtocol->print_data_as_hex(data, dataLen);

    User user;
    if (clientManager->find_user_by_account(account, user))
    {
        if(0 == user.passwd.compare(passwd))
        {
            clientManager->set_status(user.ID, LOGGED);

            int _clientFd = -1;
            if(idManager->find_clientSocketFd(user.ID, _clientFd))
            {
                idManager->delete_id(_clientFd);
            }
            idManager->add_id(user.ID, clientFd);
            loginResult = OK;
        }
    }

    // 回应登录结果
    talkProtocol->set_value_to_2Byte_buf(loginResult, res);
    result = talkProtocol->build_protocol_packet(LOGIN, res, 2, packet, sizeof(packet), packetLen);
    if (!result)
    {
        printf("login fail build res packet fail\n");
        return false;
    }

    write(clientFd, packet, packetLen);
    printf("account[%s]: login %s\n", account.c_str(), (OK==loginResult)?"success":"fail");

    return OK==loginResult;
}

/**
 * @brief 客户端消息
 * @param clientFd 对应连接的socket fd
 * @param data 消息数据
 * @param dataLen 数据长度
 * @return true 成功
 * @return false 失败
 */
bool ProtocolEngine::deal_message(int clientFd, unsigned char *data, int dataLen)
{
    u_int32_t ID = 0;
    unsigned char buf[1024] = {0};
    unsigned char packet[1024] = {0};
    int packetLen = -1;
    int len = 0;

    if (false == idManager->find_userID(clientFd, ID))
    {
        return false;
    }

    LoginStatus loginStatus = UNLOGGED;
    clientManager->get_status(ID, loginStatus);
    if (LOGGED != loginStatus)
    {
        printf("%s: ID[%u] has not logged\n", __func__, ID);
        return false;
    }

    int index = 0;
    u_int32_t desID = 0;
    u_int16_t messageLen = 0;

    talkProtocol->get_value_from_4Byte_buf(data, desID);
    index += 4;

    talkProtocol->get_value_from_2Byte_buf(data + index, messageLen);
    index += 2;

    // 发数据
    printf("%s: desID[%u]\n", __func__, desID);
    if (0 != desID)
    {
        // 发给指定客户
        int clientFd = -1;
        if(!idManager->find_clientSocketFd(desID, clientFd))
        {
            printf("%s: the desUserID[%u] have not exist\n", __func__, desID);
            return false;
        }

        talkProtocol->set_value_to_4Byte_buf(ID, buf);
        len += 4;

        talkProtocol->set_value_to_2Byte_buf(messageLen, buf + len);
        len += 2;

        memcpy(buf + len, data+index, messageLen);
        len += messageLen;

        bool result = talkProtocol->build_protocol_packet(MESSAGE, buf, len, packet, sizeof(packet), packetLen);
        if (!result)
        {
            printf("%s: build res packet fail\n", __func__);
            return false;
        }

        write(clientFd, packet, packetLen);
        printf("%s: send len[%d] to clientFd[%d] desID[%u]->ID[%u]\n", __func__, packetLen, clientFd, desID, ID);
    }
    else
    {
        // 发给所有用户
        int clientFd = -1;
        size_t size = idManager->get_id_num();

        printf("%s: id size[%lu]\n", __func__, size);

        for (size_t i = 1; i <= size; i++)
        {
            int clientFD = -1;
            std::pair<u_int32_t, int> clientFd_userID;

            clientFd_userID = idManager->get_clientFd_userID(i);
            desID = clientFd_userID.first;
            clientFd = clientFd_userID.second;

            if (ID == desID)
            {
                // 客户端数据不发送给自己
                continue;
            }

            if (clientFd > 0)
            {
                talkProtocol->set_value_to_4Byte_buf(ID, buf);
                len += 4;

                talkProtocol->set_value_to_2Byte_buf(messageLen, buf + len);
                len += 2;

                memcpy(buf + len, data+index, messageLen);
                len += messageLen;

                bool result = talkProtocol->build_protocol_packet(MESSAGE, buf, len, packet, sizeof(packet), packetLen);
                if (!result)
                {
                    printf("%s: build res packet fail\n", __func__);
                    return false;
                }

                write(clientFd, packet, packetLen);
                printf("%s: send len[%d] ID[%u]->desUserID[%u] clientFd[%d]\n", __func__, packetLen, ID, desID, clientFd);
            }
        }
    }
    return true;
}

/**
 * @brief 发布所有在线的客户信息给每一个客户
 * @return true 成功
 * @return false 失败
 */
bool ProtocolEngine::public_online_client()
{
    u_int32_t userID = 0;
    unsigned char buf[1024] = {0};
    unsigned char packet[1024] = {0};
    int packetLen = -1;
    int len = 0;

    size_t idNum = idManager->get_id_num();
    talkProtocol->set_value_to_4Byte_buf(idNum, buf);
    len += 4;

    std::pair<u_int32_t, int> clientFd_userID;
    for (size_t i = 1; i <= idNum; i++)
    {
        clientFd_userID = idManager->get_clientFd_userID(i);
        talkProtocol->set_value_to_4Byte_buf(clientFd_userID.first, buf + len);
        len += 4;

        User user = clientManager->find_userID(clientFd_userID.first);

        talkProtocol->set_value_to_2Byte_buf(user.account.size(), buf + len);
        len += 2;

        memcpy(buf + len, user.account.c_str(), user.account.size());
        len += user.account.size();
    }

    bool result = talkProtocol->build_protocol_packet(PUBLIC_CLIENT_INFO, buf, len, packet, sizeof(packet), packetLen);
    if (!result)
    {
        printf("%s: build res packet fail\n", __func__);
        return false;
    }

    for (size_t i = 1; i <= idNum; i++)
    {
        clientFd_userID = idManager->get_clientFd_userID(i);
        write(clientFd_userID.second, packet, packetLen);
        printf("%s: clientFd[%d]\n", __func__, clientFd_userID.second);
    }

    return true;
}

/**
 * @brief 解析客户端发过来包中的数据
 * @param packetData 包数据
 * @param packetDataLen 数据长度
 * @param clientFd 对应连接的socket fd
 * @return true 成功
 * @return false 失败
 */
bool ProtocolEngine::parse_packet_data(unsigned char *packetData, int packetDataLen, int clientFd)
{
    bool result = -1;
    unsigned char data[1024] = {0};
    Action action = LOGOUT;
    int dataLen = -1;

    result = talkProtocol->parse_protocol_packet(packetData, packetDataLen, action, data, sizeof(data), dataLen);
    if (false == result)
    {
        printf("parse packet error\n");
        return false;
    }

    switch (action)
    {
        case LOGIN:
        {
            result = login(clientFd, data, dataLen);
            if (result)
            {
                public_online_client();
            }

            break;
        }
        case LOGUP:
        {
            logup(clientFd, data, dataLen);
            break;
        }
        case MESSAGE:
        {
            printf("%s: MESSAGE\n", __func__);
            deal_message(clientFd, data, dataLen);
            break;
        }
        case LOGOUT:
        {
            printf("%s: LOGOUT\n", __func__);
            public_online_client();
            break;
        }
        default:
        {
            printf("action error\n");
            return false;
        }
    }
    return true;
}

ProtocolEngine::~ProtocolEngine()
{
    delete idManager;
    delete clientManager;
    delete talkProtocol;
}
